package top.infopub.security.realm;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import top.infopub.security.core.CaptchaUsernamePasswordToken;
import top.infopub.security.service.SysPurviewService;
import top.infopub.security.service.SysRoleSevice;
import top.infopub.security.service.SysUserService;
import top.infopub.security.util.SessionUserUtils;
import top.infopub.security.vo.AuthorizationVO;
import top.infopub.security.vo.PurviewVO;
import top.infopub.security.vo.RoleVO;
import top.infopub.util.MD5;
import top.infopub.util.SpringContext;
import top.infopub.zookeeper.EvnUtils;


/**
 * 自实现用户与权限查询 </br>
 * Realm实质上是一个安全相关的DAO：它封装了数据源的连接细节，并在需要时将相关数据提供给Shiro。
 * 当配置Shiro时，你必须至少指定一个Realm，用于认证和（或）授权，可以把Realm看成DataSource，即安全数据源
 * 用来告诉Shiro 用户/权限存储在哪及以何种格式存储
 * @author Awoke
 * @version 2018年9月17日
 * @see ShiroDbRealm
 * @since
 */
public class ShiroDbRealm extends AuthorizingRealm {

    private static final Logger log = LoggerFactory.getLogger(ShiroDbRealm.class);

    /**
     * 认证回调函数, 登录时调用
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
        throws AuthenticationException {
        CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken)authcToken;
        log.info("账号：{}请求登录验证，来源IP:{}", token.getUsername(), token.getHost());
        AuthorizationVO userInfo =null;
        userInfo = SpringContext.getBean(SysUserService.class).getUserByUserName(token);
        if (userInfo == null) {
            return null;
        }
        if (userInfo.isFreeze()) {
            throw new LockedAccountException("已冻结，无法登录~");
        }
        String principal = userInfo.getId() + "~~" + userInfo.getUserName();
        // 更新用户授权信息缓存
        clearCachedAuthorizationInfo(principal);
        // AuthorizationVO
        String pass = userInfo.getPassword();
        // 如果是noNeedLogin，则不需要对密码校验，可用于openid绑定后免登陆等
        if (token.getLoginForm() != null && token.getLoginForm().equalsIgnoreCase("noNeedLogin")) {
            pass = MD5.encode(pass);// 此次MD5.encode(pass)即可
            return new SimpleAuthenticationInfo(principal, pass, getName());
        }else if(token.getLoginForm()!=null&&"sso".equals(token.getLoginForm())){
            pass = MD5.encode(pass);
            return new SimpleAuthenticationInfo(principal, pass, getName());
        }
        else {
            // 获得盐
            ByteSource credentialsSalt = ByteSource.Util.bytes(token.getUsername()
                                                               + EvnUtils.getValue("md5.pwd.salt"));
            return new SimpleAuthenticationInfo(principal, pass, credentialsSalt, getName());
        }
    }

    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用  即[初始化]当前用户的权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        @SuppressWarnings("unchecked")
        Collection<String> realms = principals.fromRealm(getName());
        String principal = realms.iterator().next();

        AuthorizationVO auth = SessionUserUtils.getUserFormPrincipal(principal);
        log.info("{} 用户进行授权数据初始化到缓存...", auth.getUserName());
        List<String> roleList = new ArrayList<String>();
        List<String> permissionList = new ArrayList<String>();
        List<RoleVO> _roleList = new ArrayList<RoleVO>();
        _roleList = SpringContext.getBean(SysRoleSevice.class).queryRoleByUserId(auth.getId());
        // 获取用户角色
        if (_roleList != null && _roleList.size() > 0) {
            for (RoleVO role : _roleList) {
                roleList.add(role.getRoleCode());
            }
        }
        // 获取用户权限
        List<PurviewVO> _purviewList = new ArrayList<PurviewVO>();
        _purviewList = SpringContext.getBean(SysPurviewService.class).queryByUserId(auth.getId());
        if (_purviewList != null && _purviewList.size() > 0) {
            for (PurviewVO purview : _purviewList) {
                permissionList.add(purview.getPurviewCode());
            }
        }
        // 为当前用户设置角色和权限
        SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
        simpleAuthorInfo.addRoles(roleList);
        simpleAuthorInfo.addStringPermissions(permissionList);
        return simpleAuthorInfo;
    }

    /**
     * 更新用户授权信息缓存.
     */
    public void clearCachedAuthorizationInfo(String principal) {
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
        clearCachedAuthorizationInfo(principals);
    }

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache != null) {
            for (Object key : cache.keys()) {
                cache.remove(key);
            }
        }
    }

}
